home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 1
/
Cream of the Crop 1.iso
/
UTILITY
/
ADDMENU4.ARJ
/
ADDMENU.C
next >
Wrap
C/C++ Source or Header
|
1992-07-07
|
38KB
|
1,247 lines
/* AddMenu, v0.4.1
*
* This program is notably inefficient insofar as every change that is save is written to
* the profile file. Granted, now that we all use disk caches that's not really a problem,
* but I admit that it isn't great programming technique. I'm lazy.
*
* The program, AddMenu, is designed to let you add options to your system menus. When
* running, it hooks into the system menus to add the specified options to all menus. It
* also hooks into the keyboard and mouse to intercept requests for those options in order
* to execute those commands.
*
* Please appreciate that this is version 0. This means that it is a pre-release release.
* In other words, I appreciate that there are problems with the code, but I don't have
* time to worry about it right now. If you find bugs, please let me know, but don't
* expect a timely response. But by all means, let me know what you think.
*
* There are many notable limitations/problems with the program as it currently exists:
*
* 1. No control of the order of items on the menu.
* 2. Program currently uses both WH_CALLWNDPROC and WH_GETMESSAGE hooks. The first
* adversely affects system performance.
* 3. This program rather arbitrarily uses menu id's starting at 0xE120. This is intended
* to minimized conflicts with existing system menus (0xF000 and higher) and likely
* ids used by other programs. It is possible that these could conflict with other
* programs. By adding a line "First Id=n" to the profile, where "n" is some number,
* can control what id should be used. I didn't know how else to get around possible
* conflicts.
* 4. No help file yet.
* 5. And more, as of yet undiscovered, problems ...
*
* Program requires COMMDLG.DLL, which comes with Windows 3.1 (but can be distributed with
* the program). Program also requires AMFILTER.DLL which installs the two filters and handles
* the menu events and posts the appropriate private messages to this program.
*
* This program is copywrite of Robert M. Ryan, 1992. It is provided without warrantee
* of any sort. This program is FreeWare, and can be used and distributed for
* non-commercial use without fee providing that:
*
* a) it is not to be altered without my permission;
* b) my name remains on the package at all times;
* c) any programs which employ code taken from this program must credit me for
* the appropriate routines; and
* d) no fee is ever charged for the distribution of the program short of the cost
* of disk media and shipping cost (if any).
*
* If you want to use it for commercial purposes or have any questions about these policies,
* do not hesitate to contact me.
*
* v0.3.0, Robert M. Ryan, 28 April 1992, first public release
* v0.3.1
* - fix exit bug
* - modify so combo box works like it's supposed to
* v0.3.2
* - remove extraneous check of WM_MOVE (fixed by 0.3.1)
* - extend "executables" to include "*.exe;*.com;*.pif;*.bat"
* v0.3.3, 9 May 1992
* - Change filter to {"Programs","*.exe;*.com;*.pif;*.bat","All Files","*.*"}
* v0.3.4, 12 May 1992
* - Change "All Files" to "All Files *.*"
* v0.3.5, 7 June 1992
* - Fix bug in Browse. Initialize lpFilename to "\0".
* v0.3.6, 6 July 1992
* - Extend WinExec() processing to change drive and directory first.
* - Add error messages to WinExec() (see Run())
* - Fix bug about change of current entry
* v0.4.1, 7 July 1992
* - Simplified and debugged PM_UPDATE code to simply account for subtle
* differences between CBN_SELCHANGE and CBN_EDITUPDATE. Both don't
* change visible contents of IDD_MENUNAME, but the first actually
* doesn't update GetDlgItemText() while the second does.
* - Second public release
*
* Rob Ryan
* internet: Robert_Ryan@brown.edu
* bitnet: ST802200@BROWNVM.BITNET
* Compu$erve: 70324,227
*/
/* global defines */
#define PROGNAME "AddMenu"
#define VERSION PROGNAME " v0.4.1"
#define VERDATE __DATE__
#define PROGRAMMER "Robert M. Ryan"
#define STRING_MAX 256 /* COMMDLG requires at least 256*/
#define IDM_FIRST 0xE120 /* default first id for additions to system menu */
#define IDM_INCREMENT 0x10 /* increment between successive ids */
#define HANDLE_MAX 200 /* how many parent handles can we enumerate? */
#define BUF_SIZE 8000 /* size of buffer to hold ini stuff */
#define WILDCARD "*.exe;*.com;*.pif;*.bat" /* what wildcards for browse command, 0.3.2 */
#define TICKTHRESH 1000 /* minimium GetTickCount() interval */
enum SeparatorTypes {SEP_NONE = 0, SEP_LINE, SEP_BREAK};
/****************************************************************************
* include files
****************************************************************************/
#include <windows.h>
#include <stdarg.h>
#include <dos.h>
#include <direct.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <commdlg.h>
#include <ctype.h>
#include "addmenu.h"
#include "amfilter.h"
/****************************************************************************
* type definitions
****************************************************************************/
typedef struct tagMenuLst { /* used for linked list of menu commands */
PSTR szText;
PSTR szCommand;
struct tagMenuLst *pNext;
} MENULST;
typedef HWND FAR *LPHWND;
/****************************************************************************
* global variables
****************************************************************************/
LPSTR lpClass = PROGNAME;
HANDLE hInst;
HWND hwndMain; /* window handle of main program */
HWND hwndDialog; /* window handle of main dialog */
HWND hwndCombo; /* window handle of combobox */
FARPROC lpitMain; /* instance thunk of main dialog */
/* misc .ini variables */
LPSTR lpHidden = "Hidden";
LPSTR lpSeparator = "Separator";
LPSTR lpFirst = "First Id";
LPSTR lpOptions = "Options";
LPSTR lpProfile = "addmenu.ini";
/* misc program status variables */
BOOL bHidden; /* is icon hidden? */
WORD nSeparator; /* either SEP_NONE, SEP_LINE, or SEP_BREAK */
WORD wFirst; /* what is the first id to be used */
BOOL bChanges; /* are there any changes to save? */
BOOL bChangesMade; /* have any changes been saved? */
/* Keep track of time that command was last executed. This is here
* because Write (in Windows 3.1) apparently sends two commands. It
* is the only application that I've seen that does it, but to prevent
* problems, I check these variables and make sure that TICKTHRES msec
* have taken place before issuing the same command.
*/
DWORD dwLastCmdTicks; /* when was the last PM_MENUOPTION (in msec) */
WORD wLastCmd; /* and what was it */
/* the base for the linked list of menu options */
MENULST mlFirst = { NULL, NULL, NULL };
/* variables used while editing dialog box contents */
char szMenuName[STRING_MAX];
LPSTR lpMenuName = szMenuName;
char szPrevName[STRING_MAX];
LPSTR lpPrevName = szMenuName;
char szFilename[STRING_MAX];
LPSTR lpFilename = szFilename;
/* variables used in GetOpenFileName() from COMMDLG.H */
LPSTR lpFilter = "Programs\0" WILDCARD "\0All Files (*.*)\0*.*\0\0";
LPSTR lpPick = "Browse";
LPSTR lpExe = "exe";
/* variables use in the enumeration of windows */
LPHWND hwndArray;
int nCountArray;
int nCountWindow;
/****************************************************************************
* function prototypes
****************************************************************************/
void ReadIni(void);
LONG FAR PASCAL WndProc(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam);
int FAR PASCAL AboutDlg(HWND hDlg, WORD wMsg, WORD wParam, LONG lParam);
int FAR PASCAL MainDlg(HWND hDlg, WORD wMsg, WORD wParam, LONG lParam);
LPSTR SpaceLess(LPSTR lpStr);
BOOL Run(HWND hWnd, PSTR szFile, int nCmdShow);
void ErrorBox(HWND hWnd, PSTR szFormat, ...);
void Modal(HWND hWnd, LPSTR lpName, FARPROC lpFunc);
void WritePrivateProfileInt(LPSTR lpAppName, LPSTR lpKeyName, int nInt, LPSTR lpFileName);
int FindExactInCB(HWND hDlg, int nIdd, LPSTR lpName);
void EnableMenus(HWND hWnd);
void PickFile(HWND hWnd);
void ProcessWindows(void);
BOOL FAR PASCAL EnumWindowsFunc(HWND hWnd, DWORD lParam);
BOOL FAR PASCAL EnumChildrenFunc(HWND hWnd, DWORD lParam);
void FixWindow(HWND hWnd);
/****************************************************************************
* the code
****************************************************************************/
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS wndclass;
MSG msg;
LONG lDlgBaseUnits = GetDialogBaseUnits();
int nWidth, nHeight;
HWND hWnd;
/* if already running, make it visible if its not already, and give focus */
if (hPrevInstance != NULL) {
hWnd = FindWindow(lpClass, lpClass);
ShowWindow(hWnd, SW_RESTORE);
SetFocus(hWnd);
return 0;
}
/* read profile string parameters */
ReadIni();
hInst = hInstance;
wndclass.style = 0;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(1));
wndclass.hCursor = NULL;
wndclass.hbrBackground = COLOR_WINDOW + 1;
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = lpClass;
RegisterClass(&wndclass);
/* Make Window big enough for child dialog (exactly) */
nWidth = (WINDOW_WIDTH * LOWORD(lDlgBaseUnits)) / 4 +
GetSystemMetrics(SM_CXBORDER) * 2;
nHeight = (WINDOW_HEIGHT * HIWORD(lDlgBaseUnits)) / 8 +
GetSystemMetrics(SM_CYBORDER) * 2 +
GetSystemMetrics(SM_CYMENU) +
GetSystemMetrics(SM_CYCAPTION);
hwndMain = CreateWindow(lpClass,
lpClass,
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, /* WS_OVERLAPPEDWINDOW, */
CW_USEDEFAULT, 0,
nWidth, nHeight,
NULL, LoadMenu(hInstance, MAKEINTRESOURCE(1)),
hInstance, NULL);
/* show it as appropriate */
ShowWindow(hwndMain, nCmdShow);
SetFocus(hwndMain);
while(GetMessage(&msg, NULL, 0, 0)) {
if (IsDialogMessage(hwndDialog, &msg))
continue;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
FreeProcInstance(lpitMain);
return msg.wParam;
} /* WinMain */
/* ReadIni
*
* Read the contents from the private profile file.
*/
void ReadIni(void)
{
HANDLE hData;
LPSTR pData;
LPSTR pPtr;
char szTemp[STRING_MAX];
MENULST *pList = &mlFirst;
bHidden = GetPrivateProfileInt(lpClass, lpHidden, 0, lpProfile);
nSeparator = GetPrivateProfileInt(lpClass, lpSeparator, SEP_LINE, lpProfile);
wFirst = GetPrivateProfileInt(lpClass, lpFirst, IDM_FIRST, lpProfile);
hData = GlobalAlloc(GMEM_DISCARDABLE | GMEM_MOVEABLE, BUF_SIZE);
if (!hData) {
ErrorBox(NULL, "ReadIni: Unable to allocate memory");
return;
}
pData = GlobalLock(hData);
if (!pData) {
ErrorBox(NULL, "ReadIni: Unable to access memory");
return;
}
if (!GetPrivateProfileString(lpOptions, NULL, NULL, pData, BUF_SIZE, lpProfile)) {
GlobalUnlock(hData);
GlobalFree(hData);
return;
}
pPtr = pData;
while (*pPtr) {
pList->pNext = malloc(sizeof(MENULST));
pList = pList->pNext;
if (!pList)
return;
pList->szText = malloc(lstrlen(pPtr) + 1);
lstrcpy(pList->szText, pPtr);
if (GetPrivateProfileString(lpOptions, pPtr, "", szTemp, STRING_MAX, lpProfile)) {
pList->szCommand = malloc(lstrlen(szTemp) + 1);
lstrcpy(pList->szCommand, szTemp);
} else
pList->szCommand = NULL;
pList->pNext = NULL;
pPtr += lstrlen(pPtr) + 1;
}
GlobalUnlock(hData);
GlobalFree(hData);
} /* ReadIni */
/* WndProc
*
* The main window's callback function.
*/
LONG FAR PASCAL WndProc(HWND hWnd, WORD wMsg, WORD wParam, LONG lParam)
{
MENULST *pList;
WORD wIdd;
HMENU hMenu;
switch (wMsg) {
case WM_CREATE: /* create child (modeless) dialog */
lpitMain = MakeProcInstance((FARPROC) MainDlg, hInst);
hwndDialog = CreateDialog(hInst, MAKEINTRESOURCE(2), hWnd, lpitMain);
SetHook(hWnd);
break;
case WM_SETFOCUS: /* If get focus, give it to dialog */
SetFocus(hwndDialog);
break;
case WM_CLOSE: /* send close message to child */
PostMessage(hwndDialog, wMsg, wParam, lParam);
break;
case WM_INITMENU: /* enable menu options as appropriate based upon the */
EnableMenus(hWnd); /* contents of the child dialog */
break;
case WM_SIZE: /* if iconic, hide window if supposed to */
if (wParam == SIZEICONIC) {
SendMessage(hwndDialog, PM_QUERYSAVE, 0, 0L);
SendMessage(hwndDialog, PM_UPDATE, 0, 0L);
}
if ((wParam == SIZEICONIC) && bHidden) {
PostMessage(hwndMain, PM_HIDE, 0, 0L);
}
break;
case PM_HIDE: /* hide: called by WM_SIZE: need to use PostMessage() */
ShowWindow(hwndMain, SW_HIDE);
break;
case PM_ADDTOMENU: /* this is called by amfilter.dll for new menus */
hMenu = GetSystemMenu(wParam, 0);
if (GetMenuState(hMenu, wFirst, MF_BYCOMMAND) == (WORD) -1) {
pList = mlFirst.pNext;
wIdd = wFirst;
if (pList && (nSeparator == SEP_LINE))
AppendMenu(hMenu, MF_SEPARATOR, 0, NULL);
while (pList) { /* search linked list */
if (pList->szText)
if ((wIdd == wFirst) && (nSeparator == SEP_BREAK))
AppendMenu(hMenu, MF_STRING | MF_ENABLED | MF_MENUBARBREAK, wIdd, pList->szText);
else
AppendMenu(hMenu, MF_STRING | MF_ENABLED, wIdd, pList->szText);
wIdd += IDM_INCREMENT;
pList = pList->pNext;
}
}
break;
case PM_MENUOPTION: /* msg from dll saying that option selected from menu */
if ((wParam >= wFirst) && (wParam < SC_SIZE)) {
if (wParam == wLastCmd) /* all because of Write in Windows 3.1! */
if ((GetTickCount() - dwLastCmdTicks) < TICKTHRESH)
break;
dwLastCmdTicks = GetTickCount();
wLastCmd = wParam;
pList = mlFirst.pNext;
wIdd = wFirst;
while(pList) { /* search linked list for appropriate ID number */
if (wParam == wIdd) {
if (pList->szCommand) {
Run(NULL, pList->szCommand, SW_SHOWNORMAL);
} else
MessageBox(NULL, "This option has no command associated with it.",
"System Menu Error", MB_ICONINFORMATION | MB_OK);
break;
}
wIdd += IDM_INCREMENT; /* note: increment by 0x10 because system */
pList = pList->pNext; /* menus apparently use lower 4 bits */
}
}
break;
case WM_COMMAND: /* Pass any menu commands to dialog */
switch (wParam) {
case IDM_NEW:
case IDM_SAVE:
case IDM_DELETE:
case IDM_PICK:
case IDM_EXIT:
case IDM_ABOUT:
SendMessage(hwndDialog, wMsg, wParam, lParam);
break;
default:
return(DefWindowProc(hWnd, wMsg, wParam, lParam));
} /* switch wParam */
break;
case WM_DESTROY: /* 0.3.1: somehow this got omitted */
PostQuitMessage(0);
break;
/* 0.3.1: Since the combo box is in the child, but we're processing mouse input
* here, we have to perform the same sort of processing that DefDlgProc would.
*/
case WM_LBUTTONDOWN:
case WM_NCLBUTTONDOWN:
if ((GetFocus() == hwndCombo) || (GetParent(GetFocus()) == hwndCombo))
SendMessage(hwndCombo, CB_SHOWDROPDOWN, FALSE, 0L);
return(DefWindowProc(hWnd, wMsg, wParam, lParam));
default:
return(DefWindowProc(hWnd, wMsg, wParam, lParam));
}
return FALSE;
}
/* AboutDlg
*
* This routine displays the about dialog box.
*/
int FAR PASCAL AboutDlg(HWND hDlg, WORD wMsg, WORD wParam, LONG lParam)
{
switch (wMsg) {
case WM_INITDIALOG:
SetDlgItemText(hDlg, IDD_TITLE, VERSION);
SetDlgItemText(hDlg, IDD_DATE, VERDATE);
break;
case WM_CLOSE: /* did we manually close dlg? */
EndDialog(hDlg, FALSE);
break;
case WM_COMMAND:
switch (wParam) {
case IDD_OK: /* did we push ok? */
EndDialog(hDlg, FALSE);
break;
default:
return FALSE;
} /* switch wParam */
break;
default:
return FALSE;
} /* switch wMsg */
return TRUE;
} /* AboutDlg */
/* MainDlg
*
* This modeless dialog box provides the lion's share of the processing in
* this program. This is really the main callback function.
*/
BOOL FAR PASCAL MainDlg(HWND hDlg, WORD wMsg, WORD wParam, LONG lParam)
{
int i;
int nIndex;
MENULST *pList, *pPrev;
switch (wMsg) {
/* initialize the dialog */
case WM_INITDIALOG:
/* Fill the combobox */
pList = mlFirst.pNext;
while(pList) {
SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_ADDSTRING, 0,
(DWORD) (LPSTR) pList->szText);
pList = pList->pNext;
}
CheckDlgButton(hDlg, IDD_HIDDEN, bHidden);
switch (nSeparator) {
case SEP_NONE:
CheckDlgButton(hDlg, IDD_SEPARATOR, 0);
CheckDlgButton(hDlg, IDD_NEWCOL, 0);
break;
case SEP_LINE:
CheckRadioButton(hDlg, IDD_SEPARATOR, IDD_NEWCOL, IDD_SEPARATOR);
break;
case SEP_BREAK:
CheckRadioButton(hDlg, IDD_SEPARATOR, IDD_NEWCOL, IDD_NEWCOL);
break;
}
/* Set text limits */
SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_LIMITTEXT, STRING_MAX, 0L);
SendDlgItemMessage(hDlg, IDD_FILENAME, EM_LIMITTEXT, STRING_MAX, 0L);
/* initialize routine's variables */
bChanges = FALSE;
bChangesMade = FALSE;
nIndex = CB_ERR-1;
hwndCombo = GetDlgItem(hDlg, IDD_MENUNAME);
/* and prepare to draw screen */
PostMessage(hDlg, PM_UPDATE, 0, 0L);
break;
/* if this gets focus, pass it to menu name field */
case WM_SETFOCUS:
SetFocus(GetDlgItem(hDlg, IDD_MENUNAME));
break;
case WM_CLOSE: /* close: save changes? warn user */
SendMessage(hDlg, PM_QUERYSAVE, 0, 0L);
if (IDYES != MessageBox(hDlg, "If you quit this program, you will not be "
"able to take advantage of the System Menu additions. If you "
"wish to make use of the additions, please simply minimize "
"the program. Do you REALLY want to quit?", "Quit?",
MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2))
break;
FreeHook();
ProcessWindows();
DestroyWindow(hwndMain);
break;
case PM_QUERYSAVE: /* do you want to save? */
if (bChanges) {
if (MessageBox(hDlg, "Changes have been made."
" Do you want to save the changes?",
"Save?",
MB_YESNO | MB_ICONQUESTION) == IDYES)
SendMessage(hDlg, WM_COMMAND, IDM_SAVE, 0L);
bChanges = FALSE;
}
break;
/* fill the dialog contents and enable controls based upon the
* currently selected menuname. One of three conditions can
* hold:
* 1. The user typed something in the edit control;
* 2. The user selected some action (New, Delete, etc.)
* which changed the contents of the edit control; or
* 3. The user selected something from the list box.
* If the 3rd is the case, the text of IDD_MENUNAME hasn't changed
* yet because of a peculiarity with CBN_SELCHANGE, so we have to
* use CB_GETLBTEXT in this case. Otherwise, we can just use
* GetDlgItemText().
*/
case PM_UPDATE: /* fill in the contents of dialog */
if (wParam == CBN_SELCHANGE) {
i = SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_GETCURSEL, 0, 0L);
SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_GETLBTEXT, (WORD) i, (DWORD) lpMenuName);
} else if (!GetDlgItemText(hDlg, IDD_MENUNAME, lpMenuName, STRING_MAX))
i = CB_ERR;
else
i = FindExactInCB(hDlg, IDD_MENUNAME, lpMenuName);
if (!lstrlen(lpPrevName))
nIndex = CB_ERR-1;
lstrcpy(lpPrevName, lpMenuName);
if (nIndex == i)
break;
nIndex = i;
if (nIndex == CB_ERR) {
SetDlgItemText(hDlg, IDD_FILENAME, "");
EnableWindow(GetDlgItem(hDlg, IDD_FILENAME),
GetWindowTextLength(GetDlgItem(hDlg, IDD_MENUNAME)));
EnableWindow(GetDlgItem(hDlg, IDD_FILEPROMPT),
GetWindowTextLength(GetDlgItem(hDlg, IDD_MENUNAME)));
} else {
EnableWindow(GetDlgItem(hDlg, IDD_FILENAME), TRUE);
EnableWindow(GetDlgItem(hDlg, IDD_FILEPROMPT), TRUE);
if (GetPrivateProfileString(lpOptions, lpMenuName, "", lpFilename, STRING_MAX, lpProfile))
SetDlgItemText(hDlg, IDD_FILENAME, lpFilename);
else
SetDlgItemText(hDlg, IDD_FILENAME, "");
}
bChanges = FALSE;
break;
case WM_COMMAND:
switch (wParam) {
/* Group name (folder) combo box message */
case IDD_MENUNAME:
switch (HIWORD(lParam)) {
case CBN_SELCHANGE:
case CBN_EDITUPDATE:
SendMessage(hDlg, PM_QUERYSAVE, 0, 0L);
SendMessage(hDlg, PM_UPDATE, HIWORD(lParam), 0L);
break;
case CBN_ERRSPACE:
ErrorBox(hDlg, "MainDlg: CBN_ERRSPACE");
break;
default:
return FALSE;
}
break;
/* Any edit parameters change? */
case IDD_FILENAME:
switch (HIWORD(lParam)) {
case EN_CHANGE:
bChanges = TRUE;
break;
case EN_ERRSPACE:
ErrorBox(hDlg, "ParamDlg: EN_ERRSPACE");
break;
default:
return FALSE;
}
break;
/* Have check boxes been clicked? */
case IDD_HIDDEN: /* hidden check box used */
if (HIWORD(lParam) == BN_CLICKED) {
bHidden = !IsDlgButtonChecked(hDlg, wParam);
CheckDlgButton(hDlg, wParam, bHidden);
WritePrivateProfileInt(lpClass, lpHidden, bHidden, lpProfile);
} else
return FALSE;
break;
case IDD_SEPARATOR:
case IDD_NEWCOL:
if (HIWORD(lParam) != BN_CLICKED)
return FALSE;
ProcessWindows(); /* before farting with nSeparator, */
/* get rid of existing separators if any */
if (IsDlgButtonChecked(hDlg, wParam)) {
CheckDlgButton(hDlg, wParam, 0);
nSeparator = SEP_NONE;
} else {
CheckRadioButton(hDlg, IDD_SEPARATOR, IDD_NEWCOL, wParam);
nSeparator = (wParam == IDD_SEPARATOR ? SEP_LINE : SEP_BREAK);
}
WritePrivateProfileInt(lpClass, lpSeparator, nSeparator, lpProfile);
break;
case IDM_DELETE: /* select delete from menu */
if (GetDlgItemText(hDlg, IDD_MENUNAME, lpMenuName, STRING_MAX)) {
if (MessageBox(hDlg, "Are you sure?", "Delete",
MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION) == IDNO) {
break;
}
} else
break;
i = FindExactInCB(hDlg, IDD_MENUNAME, lpMenuName);
if (i != CB_ERR) {
SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_SETCURSEL, (WORD) -1, 0L);
if (CB_ERR == SendDlgItemMessage(hDlg, IDD_MENUNAME,
CB_DELETESTRING, i, 0L))
ErrorBox(hDlg, "Delete: invalid index");
}
SetDlgItemText(hDlg, IDD_MENUNAME, "");
/* delete from profile file */
WritePrivateProfileString(lpOptions, lpMenuName, NULL, lpProfile);
bChangesMade = TRUE;
/* remove from all system menus */
ProcessWindows();
/* delete from linked list (keep node, remove szText) */
pList = mlFirst.pNext;
pPrev = &mlFirst;
while (pList) {
if (!lstrcmp(lpMenuName, pList->szText)) {
free(pList->szText);
free(pList->szCommand);
pPrev->pNext = pList->pNext;
free(pList);
pList = pPrev->pNext;
} else {
pPrev = pList;
pList = pList->pNext;
}
}
/* and let's go back to what we were doing */
SetFocus(hDlg);
PostMessage(hDlg, PM_UPDATE, 0, 0L);
break;
case IDM_SAVE: /* select save from menu */
if (!lpPrevName[0])
break;
GetDlgItemText(hDlg, IDD_FILENAME, lpFilename, STRING_MAX);
/* if the name isn't in listbox, add it and add it to the linked list */
if (FindExactInCB(hDlg, IDD_MENUNAME, lpPrevName) == CB_ERR) {
SendDlgItemMessage(hDlg, IDD_MENUNAME, CB_ADDSTRING, 0, (DWORD) lpPrevName);
/* add item to end of linked list */
pList = &mlFirst;
while (pList->pNext)
pList = pList->pNext;
pList->pNext = malloc(sizeof(MENULST));
pList = pList->pNext;
pList->pNext = NULL;
pList->szText = malloc(lstrlen(lpPrevName)+1);
pList->szCommand = malloc(lstrlen(lpFilename)+1);
lstrcpy(pList->szText, lpPrevName);
lstrcpy(pList->szCommand, lpFilename);
ProcessWindows(); /* add to system menus */
} else { /* just update linked list entry */
pList = mlFirst.pNext;
while (pList) {
if (!lstrcmp(lpPrevName, pList->szText)) {
free(pList->szCommand);
pList->szCommand = malloc(lstrlen(lpFilename)+1);
lstrcpy(pList->szCommand, lpFilename); /* 0.3.6 */
}
pList = pList->pNext;
}
}
WritePrivateProfileString(lpOptions, lpPrevName, lpFilename, lpProfile);
bChanges = FALSE;
bChangesMade = TRUE;
/* let's go back to what we were doing */
PostMessage(hDlg, PM_UPDATE, 0, 0L);
SetFocus(hDlg);
break;
/* Close button pressed */
case IDM_EXIT:
PostMessage(hDlg, WM_CLOSE, 0, 0L);
break;
/* Pick button pressed */
case IDM_PICK:
PickFile(hDlg);
break;
/* New option selected */
case IDM_NEW:
SendMessage(hDlg, WM_COMMAND, IDD_MENUNAME,
MAKELONG(GetDlgItem(hDlg, IDD_MENUNAME), CBN_EDITUPDATE));
SetDlgItemText(hDlg, IDD_MENUNAME, "");
break;
case IDM_ABOUT:
Modal(hDlg, (LPSTR) MAKEINTRESOURCE(1), (FARPROC) AboutDlg);
break;
default:
return FALSE;
} /* switch wParam */
break;
default:
return FALSE;
} /* switch wMsg */
return TRUE;
} /* MainDlg */
/* SpaceLess
*
* Remove leading spaces and delete everything after first subsequent space
*/
LPSTR SpaceLess(LPSTR lpStr)
{
LPSTR lpPtr = lpStr;
int i;
while(lpPtr[0] == ' ') /* Remove leading spaces */
lpPtr++;
for (i = 0; i < lstrlen(lpPtr); i++) /* Remove anything after */
if (lpPtr[i] == ' ') /* any spaces */
lpPtr[i] = '\0';
return lpPtr;
} /* SpaceLess */
/* Run
*
* The WinExec() function has several notable limitations. Notably, unlike
* the program manager's "Run" command, it does not change disk or drive.
* This addresses these limitations, simulating the "Run" command.
*
* v0.3.6
*/
BOOL Run(HWND hWnd, PSTR szFile, int nCmdShow)
{
WORD wRc;
OFSTRUCT of;
char szFileName[STRING_MAX+1];
int i;
int nDrives;
BOOL bExtension = FALSE;
/* change to windows disk and directory (0.3.6) */
GetWindowsDirectory(szFileName, STRING_MAX);
if (szFileName[1] == ':')
_dos_setdrive(toupper(szFileName[0]) - 'A' + 1, &nDrives);
chdir(szFileName);
strcpy(szFileName, szFile); /* get file name */
/* add extension .exe if none */
SpaceLess(szFileName); /* remove options */
for (i = strlen(szFileName)-1; i >= 0; i--) {
if (szFileName[i] == '.') {
bExtension = TRUE;
break;
}
if (szFileName[i] == '\\')
break;
}
if (!bExtension)
strcat(szFileName, ".exe");
/* see if file exists and get full path in process */
if (OpenFile(SpaceLess(szFileName), &of, OF_EXIST) == -1) {
ErrorBox(NULL, "The file \"%s\" was not found.", of.szPathName);
return FALSE;
}
/* copy the filename into lpFileName and set of.szPathName to be just path */
lstrcpy(szFileName, of.szPathName);
for (i = lstrlen(of.szPathName)-1; i >= 0; i--)
if (of.szPathName[i] == '\\') {
of.szPathName[i] = '\0';
break;
}
/* change to that directory 0.3.6 */
if (of.szPathName[1] == ':')
_dos_setdrive(toupper(of.szPathName[0]) - 'A' + 1, &nDrives);
chdir(of.szPathName);
/* add any parameters to our new file name */
for (i = 0; i < lstrlen(szFile); i++) /* copy parameters */
if ((szFile[i] == ' ') || (szFile[i] == '/')) {
strcat(szFileName, szFile + i);
break;
}
wRc = WinExec(szFileName, nCmdShow);
if (wRc > 32)
return TRUE;
switch (wRc) {
case 0:
ErrorBox(hWnd, "System was out of memory, executable file was corrupt, or relocations were invalid.");
break;
case 2: /* this should be caught by above processing */
ErrorBox(hWnd, "File was not found. (%s)", szFile);
break;
case 3: /* this should be caught by above processing */
ErrorBox(hWnd, "Path was not found. (%s)", szFile);
break;
case 5:
ErrorBox(hWnd, "Attempt was made to dynamically link to a task, or there was a sharing or network-protection error.");
break;
case 6:
ErrorBox(hWnd, "Library required separate data segments for each task.");
break;
case 8:
ErrorBox(hWnd, "There was insufficient memory to start the application. (%s)", szFile);
break;
case 10:
ErrorBox(hWnd, "Windows version was incorrect.");
break;
case 11:
ErrorBox(hWnd, "Executable file was invalid. Either it was not a Windows application or there was an error in the .EXE image. (%s)", szFile);
break;
case 12:
ErrorBox(hWnd, "Application was designed for a different operating system.");
break;
case 13:
ErrorBox(hWnd, "Application was designed for MS-DOS 4.0. (%s)", szFile);
break;
case 14:
ErrorBox(hWnd, "Type of executable file was unknown. (%s)", szFile);
break;
case 15:
ErrorBox(hWnd, "Attempt was made to load a real-mode application (developed for an earlier version of Windows).");
break;
case 16:
ErrorBox(hWnd, "Attempt was made to load a second instance of an executable file containing multiple data segments that were not marked read-only.");
break;
case 19:
ErrorBox(hWnd, "Attempt was made to load a compressed executable file. The file must be decompressed before it can be loaded.");
break;
case 20:
ErrorBox(hWnd, "Dynamic-link library (DLL) file was invalid. One of the DLLs required to run this application was corrupt.");
break;
case 21:
ErrorBox(hWnd, "Application requires Microsoft Windows 32-bit extensions.");
break;
default:
ErrorBox(hWnd, "Unknown error (%d)", wRc);
break;
}
return FALSE;
} /* Run */
/* ErrorBox
*
* Print an error box message. Accepts a variable number of arguments
* according to the ANSI standard. The szFormat string is a simple format
* string like used with printf, permitting the use of "%d" (int), "%ld"
* (long int), "%f" (double), "%s" (char *), and "%Fs" (LPSTR).
*/
void ErrorBox(HWND hWnd, PSTR szFormat, ...)
{
va_list args;
char szString[STRING_MAX];
va_start(args, szFormat);
vsprintf(szString, szFormat, args);
MessageBox(hWnd, szString, "Error", MB_ICONEXCLAMATION | MB_OK);
return;
} /* ErrorBox */
/* Modal
* This routine is the constructor for a modal dialog box. Note that
* with this definition, merely creating a item of type "ModalDialog"
* will create a thunk instance and create the box. The thunk is freed
* in when done and the focus is returned to what is was on entry.
* Note that there is no destructor because the instance is freed in
* this routine (this is so the focus window could be recorded and
* restored upon termination).
*/
void Modal(HWND hWnd, LPSTR lpName, FARPROC lpFunc)
{
FARPROC lpitDialog;
HWND hWndFocus = GetFocus();
if (!(lpitDialog = MakeProcInstance(lpFunc, hInst)))
ErrorBox(hWnd, "Modal constructor: MakeProcInstance failure");
if (DialogBox(hInst, lpName, hWnd, lpitDialog) == -1)
ErrorBox(hWnd, "Modal constructor: Unable to create dialog");
FreeProcInstance(lpitDialog);
SetFocus(hWndFocus);
} /* Modal */
/* WritePrivateProfileInt
*
* While the system provides both GetProfileString and GetProfileInt,
* it only provide WriteProfileString. This is the logical partner.
*/
void WritePrivateProfileInt(LPSTR lpAppName, LPSTR lpKeyName, int nInt, LPSTR lpFileName)
{
static char szNumber[80];
itoa(nInt, szNumber, 10);
WritePrivateProfileString(lpAppName, lpKeyName, szNumber, lpFileName);
} /* WritePrivateProfileInt */
/* FindExactinCB
*
* The command to look up string in Combobox isn't too picky. Let's find
* the one which precisely matches the string we're looking for.
*/
int FindExactInCB(HWND hDlg, int nIdd, LPSTR lpName)
{
int i;
char szString[STRING_MAX];
LPSTR lpString = szString;
i = SendDlgItemMessage(hDlg, nIdd, CB_FINDSTRING, (WORD) -1, (DWORD) lpName);
if (i != CB_ERR)
SendDlgItemMessage(hDlg, nIdd, CB_GETLBTEXT, i, (DWORD) lpString);
if (lstrcmp(lpString, lpName))
return CB_ERR;
return i;
} /* FindStringInCB */
void EnableMenus(HWND hWnd)
{
HMENU hMenu;
int i;
hMenu = GetMenu(hWnd);
i = GetWindowTextLength(GetDlgItem(hwndDialog, IDD_MENUNAME));
if (i) {
EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_DELETE, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_PICK, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(hMenu, IDM_NEW, MF_BYCOMMAND | MF_ENABLED);
} else {
EnableMenuItem(hMenu, IDM_SAVE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_DELETE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_PICK, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, IDM_NEW, MF_BYCOMMAND | MF_GRAYED);
}
/* this program can be neither maximized nor resized */
hMenu = GetSystemMenu(hWnd, 0);
EnableMenuItem(hMenu, SC_SIZE, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(hMenu, SC_MAXIMIZE, MF_BYCOMMAND | MF_GRAYED);
return;
} /* EnableMenus */
void PickFile(HWND hWnd)
{
OPENFILENAME ofn;
lpFilename[0] = '\0';
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.hwndOwner = hWnd;
ofn.hInstance = hInst;
ofn.lpstrFilter = lpFilter;
ofn.lpstrCustomFilter = NULL;
ofn.nMaxCustFilter = 0;
ofn.nFilterIndex = 1;
ofn.lpstrFile = lpFilename;
ofn.nMaxFile = STRING_MAX;
ofn.lpstrFileTitle = NULL;
ofn.nMaxFileTitle = 0;
ofn.lpstrInitialDir = NULL;
ofn.lpstrTitle = lpPick;
ofn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
ofn.nFileOffset = 0;
ofn.nFileExtension = 0;
ofn.lpstrDefExt = lpExe;
ofn.lCustData = 0;
ofn.lpfnHook = NULL;
ofn.lpTemplateName = NULL;
if (GetOpenFileName(&ofn))
SetDlgItemText(hWnd, IDD_FILENAME, lpFilename);
} /* PickFile */
void ProcessWindows(void)
{
FARPROC lpit = MakeProcInstance(EnumWindowsFunc, hInst);
HANDLE hData;
int i;
if (!lpit) {
ErrorBox(hwndMain, "ProcessWindows: Error making instance thunk (1).");
return;
}
hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(HWND) * HANDLE_MAX);
if (!hData) {
ErrorBox(hwndMain, "ProcessWindows: Error creating data buffer.");
return;
}
hwndArray = (LPHWND) GlobalLock(hData);
if (!hwndArray) {
ErrorBox(hwndMain, "ProcessWindows: Error accessing data buffer.");
return;
}
nCountArray = 0;
EnumWindows(lpit, 0L);
FreeProcInstance(lpit);
lpit = MakeProcInstance(EnumChildrenFunc, hInst);
if (!lpit) {
ErrorBox(hwndMain, "ProcessWindows: Error making instance thunk (2).");
return;
}
nCountWindow = 0;
for (i = 0; i < nCountArray; i++)
EnumChildWindows(hwndArray[i], lpit, 0L);
FreeProcInstance(lpit);
#if defined(DEBUG)
ErrorBox(hwndMain, "There were %d parents and %d children.", nCountArray, nCountWindow);
#endif
GlobalUnlock(hData);
GlobalFree(hData);
return;
} /* ProcessWindows */
BOOL FAR PASCAL EnumWindowsFunc(HWND hWnd, DWORD lParam)
{
if (nCountArray >= HANDLE_MAX) {
MessageBox(hwndMain, "Too many parents!", "Error", MB_ICONSTOP | MB_OK);
return 0;
}
if (hWnd) {
hwndArray[nCountArray] = hWnd;
nCountArray++;
}
FixWindow(hWnd);
return TRUE;
} /* EnumWindowsFunc */
BOOL FAR PASCAL EnumChildrenFunc(HWND hWnd, DWORD lParam)
{
nCountWindow++;
FixWindow(hWnd);
return TRUE;
} /* EnumTaskWindows */
void FixWindow(HWND hWnd)
{
HMENU hMenu;
MENULST *pList;
WORD wIdd;
int i;
if (!hWnd) {
return; /* if no window, quit */
}
if (!(GetWindowLong(hWnd, GWL_STYLE) & WS_SYSMENU)) {
return; /* if no system menu in window, quit */
}
hMenu = GetSystemMenu(hWnd, 0);
pList = mlFirst.pNext;
wIdd = wFirst;
/* We're deleting all of them, so then do it. Note that we also want to remove the
* separator too, so this also removes the menu item before the first one found.
* While this logic sound confusing, it is necessary to make sure that we haven't
* deleted the first menu item.
*/
if ((GetMenuState(hMenu, wIdd, MF_BYCOMMAND) != (WORD) -1) && (nSeparator == SEP_LINE)) {
for (i = 1; i < GetMenuItemCount(hMenu); i++)
if (GetMenuItemID(hMenu, i) == wIdd)
DeleteMenu(hMenu, i-1, MF_BYPOSITION);
}
while (pList) {
if (GetMenuState(hMenu, wIdd, MF_BYCOMMAND) != (WORD) -1)
DeleteMenu(hMenu, wIdd, MF_BYCOMMAND);
pList = pList->pNext;
wIdd += IDM_INCREMENT;
}
return;
} /* FixWindow */